package com.tenbent.product.base.config.datasource;

import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.logging.Log4jFilter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceStatLogger;
import com.alibaba.druid.pool.DruidDataSourceStatLoggerImpl;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.Log4jImpl;
import com.alibaba.druid.wall.WallFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Arrays;

/**
 * 数据源配置 (开启监控，日志输出)
 *
 * 监控入口地址： http://localhost:8080/druid/index.html
 *
 * 通过配置在WebApplicationInitializer 的实现类(MyServletWebApplicationInitializer)
 *
 * 配置 WebStatFilter ,StatViewServlet 开启数据源监控，用户名，密码也配置于此处。
 *
 * @author Randy
 *
 *         Created by ThinkPad on 2017/7/25.
 */
@Configuration
public class DataSourceConfig {

	@Autowired
	Environment env;

	// 占位符方式
	@Value("${jdbc.driver}")
	private String driverClass;
	@Value("${jdbc.url}")
	private String url;
	@Value("${jdbc.username}")
	private String userName;
	@Value("${jdbc.password}")
	private String passWord;
	@Value("${jdbc.autoCommit}")
	private boolean autoCommit;
	@Value("${jdbc.initialSize}")
	private int initialSize;
	@Value("${jdbc.minIdle}")
	private int minIdle;
	@Value("${jdbc.maxActive}")
	private int maxActive;

	// @Bean(destroyMethod = "close")
	// public DataSource dataSource() {
	//
	// BasicDataSource dataSource = new BasicDataSource();
	// // 第一种获取属性的方式
	// // dataSource.setDriverClassName(env.getProperty("jdbc.driver"));
	// // dataSource.setUrl(env.getProperty("jdbc.url"));
	// // dataSource.setUsername(env.getProperty("jdbc.username"));
	// // dataSource.setPassword(env.getProperty("jdbc.password"));
	// // 第二中获取属性的方式 (推荐方式)
	// dataSource.setDriverClassName(driverClass);
	// dataSource.setUrl(url);
	// dataSource.setUsername(userName);
	// dataSource.setPassword(passWord);
	// return dataSource;
	// }

	@Bean(initMethod = "init", destroyMethod = "close")
	public DataSource dataSource() throws SQLException {
		DruidDataSource dataSource = new DruidDataSource();
		/** 基本属性 name、url、user、password (name 属性用来标识在多数据源的时候在监控是用于区分) **/
		dataSource.setName("ds1");
		dataSource.setUrl(url);
		dataSource.setDriverClassName(driverClass);
		dataSource.setUsername(userName);
		dataSource.setPassword(passWord);
		dataSource.setDefaultAutoCommit(autoCommit);

		/** 配置初始化大小、最小、最大 **/
		dataSource.setInitialSize(initialSize);
		dataSource.setMinIdle(minIdle);
		dataSource.setMaxActive(maxActive);

		/**
		 * 配置获取连接等待超时的时间 (如果配置了 maxWait 属性默认开启公平锁，并发效率会有所下降，可以通过设置useUnfairLock
		 * 使用非公平锁，并发会有所提高)
		 **/
		dataSource.setMaxWait(60000);
		dataSource.setUseUnfairLock(true);

		/** 配置一个连接在池中最小生存的时间，单位是毫秒 **/
		dataSource.setMinEvictableIdleTimeMillis(300000);

		/** 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒 **/
		dataSource.setTimeBetweenEvictionRunsMillis(60000);
		/** 检查sql语句 **/
		dataSource.setValidationQuery("SELECT 'x'");

		/**
		 * 建议配置为true，不影响性能，并且保证安全性。申请连接的时候检测，如果空闲时间大于
		 * timeBetweenEvictionRunsMillis， 执行validationQuery检测连接是否有效。
		 **/
		dataSource.setTestWhileIdle(true);
		/** 申请连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能。 **/
		dataSource.setTestOnBorrow(false);
		/** 归还连接时执行validationQuery检测连接是否有效，做了这个配置会降低性能 **/
		dataSource.setTestOnReturn(false);

		/** 打开PSCache，并且指定每个连接上PSCache的大小 **/
		dataSource.setPoolPreparedStatements(true);
		/** 要启用PSCache，必须配置大于0，当大于0时，poolPreparedStatements自动触发修改为true。 **/
		dataSource.setMaxPoolPreparedStatementPerConnectionSize(50);

		/**
		 * 监控统计用的filter:stat,日志用的filter:log4j,防御sql注入的filter:wall
		 * 
		 * (filters=stat,wall,log4j)
		 *
		 * 通过别名的方式配置 方式一
		 **/
		// dataSource.setFilters("stat");
		/** 通过代理Bean 的方式二 */
		Filter[] filters = { statFilter(), log4jFilter(), wallFilter() };
		dataSource.setProxyFilters(Arrays.asList(filters));
		/** 每隔30分钟把监控记录写入日志 1000 * 60 * 30 **/
		dataSource.setTimeBetweenLogStatsMillis(1000 * 60 * 30);
		dataSource.setStatLogger(statLogger());

		return dataSource;
	}

	/**
	 * druid 监控过滤器
	 **/
	@Bean
	public StatFilter statFilter() {
		StatFilter statFilter = new StatFilter();
		/** 合并相同的SQL **/
		statFilter.setMergeSql(true);
		/** 记录日志 **/
		statFilter.setLogSlowSql(true);
		/** 设置大于 5秒的sql就算慢SQL **/
		statFilter.setSlowSqlMillis(5000);
		return statFilter;
	}

	/**
	 * druid 防止sql注入
	 */
	@Bean
	public WallFilter wallFilter() {
		WallFilter wallFilter = new WallFilter();
		wallFilter.setDbType("mysql");
		return wallFilter;
	}

	/**
	 * druid 日志过滤器
	 **/
	@Bean
	public Log4jFilter log4jFilter() {
		Log4jFilter log4jFilter = new Log4jFilter();
		/** 如果为true 则输出全部日志，太大一般为false **/
		log4jFilter.setResultSetLogEnabled(false);
		log4jFilter.setStatementExecutableSqlLogEnable(true);
		log4jFilter.setConnectionLogErrorEnabled(true);
		log4jFilter.setStatementLogErrorEnabled(true);
		log4jFilter.setStatementParameterSetLogEnabled(true);
		return log4jFilter;
	}

	/** druid 监控记录输出到日志中 **/
	@Bean
	public DruidDataSourceStatLogger statLogger() {
		DruidDataSourceStatLogger statLogger = new DruidDataSourceStatLoggerImpl();
		statLogger.setLogger(Log4jImpl());
		return statLogger;
	}

	/** druid 使用log4j记录监控日志(druid.statlog 表示log4j.xml logger name ) **/
	@Bean
	public Log Log4jImpl() {
		return new Log4jImpl("druid.statlog");
	}

}
